use regex::Regex;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
pub struct FileSystem {
    pub name: String,
    pub directories: Option<Vec<Rc<RefCell<FileSystem>>>>, // 子目录列表
    pub files: Option<Vec<Rc<RefCell<FileSystem>>>>,       // 文件列表
    pub content: Option<String>,                           // 文件内容（如果是文件的话）
    pub opt_time: Option<String>,                          // 操作时间
}

#[derive(Serialize, Deserialize, Debug)]
pub struct SerializableFileSystem {
    pub name: String,
    pub directories: Option<Vec<SerializableFileSystem>>,
    pub files: Option<Vec<SerializableFileSystem>>,
    pub content: Option<String>,
    pub opt_time: Option<String>,
}

impl FileSystem {
    pub fn new(name: &str, content: Option<String>, opt_time: Option<String>) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(FileSystem {
            name: name.to_string(),
            directories: None,
            files: None,
            content,
            opt_time,
        }))
    }

    pub fn add_directory(&mut self, directory: Rc<RefCell<FileSystem>>) {
        if self.directories.is_none() {
            self.directories = Some(Vec::new());
        }

        if let Some(directories) = &mut self.directories {
            directories.push(directory);
        }
    }

    pub fn add_file(&mut self, file: Rc<RefCell<FileSystem>>) {
        if self.files.is_none() {
            self.files = Some(Vec::new());
        }

        if let Some(files) = &mut self.files {
            files.push(file);
        }
    }

    pub fn to_serializable(&self) -> SerializableFileSystem {
        SerializableFileSystem {
            name: self.name.clone(),
            content: self.content.clone(),
            opt_time: self.opt_time.clone(),
            directories: self.directories.as_ref().map(|dirs| {
                dirs.iter()
                    .map(|dir| dir.borrow().to_serializable())
                    .collect()
            }),
            files: self.files.as_ref().map(|files| {
                files.iter()
                    .map(|file| file.borrow().to_serializable())
                    .collect()
            }),
        }
    }
}

pub fn parse_text_to_json(text: &str) -> Rc<RefCell<FileSystem>> {
    let re1 = Regex::new(r"[│   ├── └── ](?P<indent>\s*+)").unwrap();
    let re2 = Regex::new(r"[│   ├── └── ](?P<indent>\s*+)(?P<name>[^\s│   ├─ └── ]+(?:\s+(![│   ├─ └── ]))?)").unwrap();

    let root = FileSystem::new("add", None, None);

    let stack = Rc::new(RefCell::new(vec![(0, root.clone())])); // Stack to manage indentation level and corresponding node

    for line in text.lines() {
        let captures = re1.captures(line);
        if let Some(caps) = captures {
            let indent = caps.name("indent").unwrap().as_str();
            let indent_level = if indent.len() > 0 { indent.len() } else { 1 };
            let name = re2.captures(line).unwrap().name("name").unwrap().as_str().trim();
            // Here we can decide on the content and opt_time if needed
            let content = if name.contains(".") {
                Some(format!("File content for {}", name)) // Example: setting content for .rs files
            } else {
                None // Directories don't have content
            };

            let opt_time = Some("2024-12-06T12:00:00Z".to_string()); // Example: a fixed timestamp for demonstration

            let new_node = FileSystem::new(name, content, opt_time);

            // Ensure we are at the correct level of indentation
            let stack_c0 = stack.clone();
            let mut stack_mut = stack_c0.borrow_mut();

            while stack_mut.len() > 0 && indent_level <= stack_mut.last().unwrap().0 {
                stack_mut.pop();
            }

            let last_node = Rc::clone(&stack_mut[stack_mut.len() - 1].1);
            let mut last_node_mut = last_node.borrow_mut();

            if name.contains(".") {
                last_node_mut.add_file(new_node.clone()); // Add to files if it's a file
            } else {
                last_node_mut.add_directory(new_node.clone()); // Add to directories if it's a directory
            }
            // Now, safely push the new node onto the stack
            let last_node_rc = if let Some(dirs) = last_node_mut.directories.as_deref_mut() {
                if let Some(last_dir) = dirs.last() {
                    Rc::clone(last_dir) // Clone the last directory in the list
                } else {
                    Rc::clone(&last_node)
                }
            } else {
                Rc::clone(&last_node)
            };
            stack_mut.push((indent_level, last_node_rc));
        }
    }
    root
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_parse_text_to_json() {
        let input = r"
add
├── Cargo.toml
├── src
│   ├── lib.rs
│   └── add.rs
└── tests
   └── test_add.rs
";

    // Parse the text into a file system structure
    let root = parse_text_to_json(input);
    // println!("{:?}", root);

    let serializable_root = root.borrow().to_serializable();

    // Convert the structure to JSON
        let json = serde_json::to_string_pretty(&serializable_root).unwrap();
            println!("{}", json);
    }
}
